package root.electronclass.electronclass.common.base

import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import java.lang.reflect.ParameterizedType

/**
 *
 * @author Lokiy
 * @date 2023/1/8 13:30
 */
abstract class BasicAdapter<T, Binding : ViewBinding> : RecyclerView.Adapter<BindingHolder<Binding>>() {

    private val bindingClass by lazy { (this::class.java.genericSuperclass as ParameterizedType).actualTypeArguments[1] as Class<*> }
    private val inflateMethod by lazy {
        bindingClass.getDeclaredMethod(
            "inflate",
            LayoutInflater::class.java,
            ViewGroup::class.java,
            Boolean::class.java
        )
    }

    private val data: MutableList<T> = mutableListOf()
    val list: MutableList<T>
        get() = data.toMutableList()

    override fun getItemCount(): Int = data.size

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindingHolder<Binding> {
        @Suppress("UNCHECKED_CAST")
        return BindingHolder((inflateMethod.invoke(this, LayoutInflater.from(parent.context), parent, false) as Binding))
    }

    override fun onBindViewHolder(holder: BindingHolder<Binding>, position: Int) {
        onBindViewHolder(holder, data.getOrNull(position))
    }

    abstract fun onBindViewHolder(holder: BindingHolder<Binding>, item: T?)

    open fun addAll(list: Collection<T>?) {
        list ?: return
        notifyItemRangeInserted(data.size, data.addAll(list).let { data.size })
    }

    open fun replaceAll(list: Collection<T>?) {
        data.clear()
        list?.let {
            data.addAll(list)
        }
        notifyDataSetChanged()
    }


    open fun refresh(list: Collection<T>?) {
        list ?: return notifyItemRangeRemoved(0, data.size.also { data.clear() })
        val old = data.toMutableList()
        data.clear()
        data.addAll(list)
        val new = data.toMutableList()
        DiffUtil.calculateDiff(object : DiffUtil.Callback() {
            override fun getOldListSize(): Int = old.size

            override fun getNewListSize(): Int = new.size

            override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
                return old.getOrNull(oldItemPosition).hashCode() == new.getOrNull(newItemPosition).hashCode()
            }

            override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
                return old[oldItemPosition] == new[newItemPosition]
            }

        }).dispatchUpdatesTo(this)
    }

    @SuppressLint("NotifyDataSetChanged")
    open fun notifyChanged(list: Collection<T>?) {
        list ?: return notifyItemRangeRemoved(0, data.size.also { data.clear() })
        data.clear()
        data.addAll(list)
        notifyDataSetChanged()
    }

}

class BindingHolder<Binding : ViewBinding>(val binding: Binding) : RecyclerView.ViewHolder(binding.root)

@Suppress("FunctionName")
inline fun <T, reified Binding : ViewBinding> BasicAdapter(
    crossinline onLoadMore: () -> Unit = {},
    pageSize: Int = 20,
    crossinline onViewRecycled:(BindingHolder<Binding>) -> Unit = {},
    crossinline onBindHolder: BasicAdapter<T, Binding>.(BindingHolder<Binding>, T) -> Unit
): BasicAdapter<T, Binding> =
    object : BasicAdapter<T, Binding>() {
        private var isOnLoadMore = false
        private var isLoadEnd = false

        override fun onBindViewHolder(holder: BindingHolder<Binding>, item: T?) {
            item ?: return
            onBindHolder.invoke(this, holder, item)
        }

        override fun getItemViewType(position: Int): Int {
            if (position >= itemCount - 2 && isOnLoadMore.not() && isLoadEnd.not()) {
                isOnLoadMore = true
                onLoadMore.invoke()
            }
            return super.getItemViewType(position)
        }

        override fun onViewRecycled(holder: BindingHolder<Binding>) {
            super.onViewRecycled(holder)
            onViewRecycled.invoke(holder)
        }

        override fun addAll(list: Collection<T>?) {
            isOnLoadMore = false
            isLoadEnd = (list?.size ?: 0) < pageSize
            super.addAll(list)
        }

        override fun refresh(list: Collection<T>?) {
            isOnLoadMore = false
            isLoadEnd = (list?.size ?: 0) < pageSize
            super.refresh(list)
        }

        override fun notifyChanged(list: Collection<T>?) {
            isOnLoadMore = false
            isLoadEnd = (list?.size ?: 0) < pageSize
            super.notifyChanged(list)
        }

    }